home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 7 / Amiga Format AFCD07 (Dec 1996, Issue 91).iso / serious / shareware / programming / ixemul-complete / ixemul / utils / ixtrace.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-08-14  |  12.4 KB  |  556 lines

  1. /*
  2.  *  This file is part of ixemul.library for the Amiga.
  3.  *  Copyright (C) 1991, 1992  Markus M. Wild
  4.  *  Changed to avoid buffer overflows by J. Hoehle and
  5.  *  restricted output length for: str(n)cat, strlen
  6.  *
  7.  *  This library is free software; you can redistribute it and/or
  8.  *  modify it under the terms of the GNU Library General Public
  9.  *  License as published by the Free Software Foundation; either
  10.  *  version 2 of the License, or (at your option) any later version.
  11.  *
  12.  *  This library is distributed in the hope that it will be useful,
  13.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15.  *  Library General Public License for more details.
  16.  *
  17.  *  You should have received a copy of the GNU Library General Public
  18.  *  License along with this library; if not, write to the Free
  19.  *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  20.  */
  21.  
  22. #include <stdio.h>
  23. #include <stdlib.h>
  24. #include <proto/alib.h>
  25. #include <unistd.h>
  26. #include <string.h>
  27. #include <sys/tracecntl.h>
  28. #include <signal.h>
  29. #include <fcntl.h>
  30. #include <sys/ioctl.h>
  31. #include <sys/ioctl_compat.h>
  32. #include <sys/termios.h>
  33. #include <exec/types.h>
  34. #include <libraries/dos.h>
  35. #include <proto/exec.h>
  36. #include "ixtrace.h"
  37.  
  38. #define OUT_WIDTH  80    /* big enough (>30) to hold first information */
  39.  
  40. static void print_call (FILE *output, struct trace_packet *tp);
  41.  
  42. int print_all = 0;
  43. int skip_sigsetmask = 0;
  44. int skip_calls = 0;
  45. FILE *output;
  46. char VERSION[] = "$VER: ixtrace 1.4 (14-Aug-96)";
  47.  
  48. void
  49. dummy_handler ()
  50. {
  51. }
  52.  
  53. int
  54. main (int argc, char *argv[])
  55. {
  56.   char *logfile = "-";
  57.   struct trace_packet tp;
  58.   struct trace_packet *tpz;
  59.   int *tpz_;
  60.   struct MsgPort *mp;
  61.   int c, in = 0;
  62.  
  63.   memset ((char *) &tp, '\000', sizeof (tp));
  64.   signal (SIGMSG, dummy_handler);
  65.  
  66.   while ((c = getopt (argc, argv, "ailvmzo:c:p:s:n:")) != EOF)
  67.     switch (c)
  68.       {
  69.       case 'a':
  70.     print_all = 1;
  71.     break;
  72.  
  73.       case 'i':
  74.     in = 1;
  75.     break;
  76.  
  77.       case 'l':                    /* list system calls */
  78.     {
  79.         int i;
  80.  
  81.         for(i=1;i<=MAXCALLS;i=i+2)
  82.         {
  83.         fprintf(stdout,"(%3d) %-25s\t(%3d) %-25s\n",i,call_table[i].name,
  84.                                                   i+1,call_table[i+1].name);
  85.         }
  86.     return 0;
  87.     }
  88.     break;
  89.  
  90.       case 'c':                    /* system call by name */
  91.     {
  92.         int i;
  93.         int notfound=1;    /* Just to make sure that we know if call is found */
  94.         char *callname;
  95.  
  96.         callname=optarg;
  97.  
  98.         for(i=1;i<=MAXCALLS;i++)
  99.         {
  100.             if (!strcmp(callname, call_table[i].name))
  101.                 {    tp.tp_syscall = i;
  102.                     notfound=0;
  103.                     break;
  104.                 }        
  105.         }
  106.         if (notfound)
  107.         {
  108.              fprintf(stderr,"system call [%s] is unknown to ixtrace\n",callname);
  109.              return 1;
  110.         }
  111.     }
  112.     break;
  113.  
  114.       case 'm':
  115.     skip_sigsetmask = 1;
  116.     break;
  117.  
  118.       case 'o':
  119.     logfile = optarg;
  120.     break;
  121.  
  122.       case 'p':
  123.     tp.tp_pid = atoi (optarg);
  124.     break;
  125.     
  126.       case 'n':
  127.     skip_calls = atoi (optarg);
  128.     if (skip_calls < 0)
  129.     {
  130.       fprintf(stderr, "invalid argument for option -n\n");
  131.       exit(1);
  132.     }
  133.     break;
  134.     
  135.       case 's':
  136.     if (!isdigit(optarg[0]))
  137.     {
  138.       fprintf(stderr, "The -s option requires a number\n",MAXCALLS);
  139.       exit(1);
  140.     }
  141.         tp.tp_syscall = atoi (optarg);
  142.     if (tp.tp_syscall > MAXCALLS)
  143.     {
  144.       fprintf(stderr, "System call number is out of range 1-%d\n",MAXCALLS);
  145.       exit(1);
  146.     }
  147.         break;
  148.  
  149.       case 'z':                    /* you name the calls --in testing-- */
  150.     {
  151.         int i;
  152.         char calls[80]="";
  153.         int notfound=1;
  154.  
  155.         /* Right now this is only the beginning, clear all systems calls.
  156.            In other words, make them all non-interesting.                    */
  157.         for(i=1;i<=MAXCALLS;i++)
  158.         {
  159.           call_table[i].interesting=0;
  160.         }
  161.         fprintf(stdout,"When done enter \"x\" by itself, followed by a [RETURN]\n");
  162.     do {
  163.         fprintf(stdout,"trace > ");
  164.         gets(calls);
  165.         if (!strcmp("x",calls)) break;
  166.             for(i=1;i<=MAXCALLS;i++)
  167.             {
  168.                 if (!strcmp(calls, call_table[i].name))
  169.                     {    call_table[i].interesting=1;
  170.                         notfound=0;
  171.                         break;
  172.                     }        
  173.             }
  174.             if (notfound) fprintf(stderr,"[%s] is unknown to ixtrace, try again\n"
  175.                                         , calls);
  176.         notfound=1;
  177.         } while(strcmp("x",calls)); /* A lil' over-kill */
  178.  
  179.     }
  180.     break;
  181.       case 'v':
  182.     {
  183.       fprintf(stdout, "%s\n",VERSION+6); /* get rid of the first 7 chars */
  184.       return 0;
  185.     }
  186.         break;
  187.  
  188.       default:
  189.     fprintf (stderr, "%s [-a] [-m] [-l] [-v] [-z] [-c syscall-name] [-n N] [-o logfile] [-p pid] [-s syscall-number]\n", argv[0]);
  190.     fprintf (stderr, "  -a  trace all calls (else __-calls are skipped)\n");
  191.     fprintf (stderr, "  -m  skip sigsetmask() calls (they're heavily used inside the library)\n");
  192.     fprintf (stderr, "  -i  trace entry to functions. Default is exit.\n");
  193.     fprintf (stderr, "  -l  list system calls (syscall #) [syscall name]\n");
  194.     fprintf (stderr, "  -v  version\n");
  195.     fprintf (stderr, "  -z  only trace the syscalls you want to trace\n");
  196.     fprintf (stderr, "  -c  only trace this syscall (by name)\n");
  197.     fprintf (stderr, "  -n  skip the first N traces\n");
  198.     fprintf (stderr, "  -o  log output to logfile (default is stdout)\n");
  199.     fprintf (stderr, "  -p  only trace process pid (default is to trace all processes)\n");
  200.     fprintf (stderr, "  -s  only trace this syscall (default is to trace all calls)\n");
  201.  
  202.         return 1;
  203.       }
  204.  
  205.   if (logfile[0] == '-' && !logfile[1])
  206.     output = stdout;
  207.   else
  208.     output = fopen (logfile, "w");
  209.  
  210.   if (!output)
  211.     {
  212.       perror ("fopen");
  213.       return 1;
  214.     }
  215.  
  216.   if ((mp = CreatePort (0, 0)))
  217.     {
  218.       tp.tp_tracer_port = mp;
  219.       if (tracecntl (TRACE_INSTALL_HANDLER, &tp) == 0)
  220.     {
  221.       while (1)
  222.         {
  223.           struct Message *msg;
  224.           long sigs;
  225.  
  226.           sigs = Wait ((1 << mp->mp_SigBit) | SIGBREAKF_CTRL_C);
  227.           while ((msg = GetMsg (mp)))
  228.         {
  229.               if (msg != (struct Message *) &tp)
  230.             {
  231.               fprintf (stderr, "Got alien message! Don't do that ever again ;-)\n");
  232.             } 
  233.               else
  234.             {
  235.               if (in)
  236.             tp.tp_action = TRACE_ACTION_JMP;
  237.               if (! tp.tp_is_entry || tp.tp_action == TRACE_ACTION_JMP)
  238.                 print_call (output, &tp);
  239.             }
  240.               Signal ((struct Task *) msg->mn_ReplyPort, SIGBREAKF_CTRL_E);
  241.             }
  242.           if (sigs & SIGBREAKF_CTRL_C)
  243.         break;
  244.         }
  245.       tracecntl (TRACE_REMOVE_HANDLER, &tp);
  246.     }
  247.       else
  248.     perror ("tracecntl");
  249.       
  250.       DeletePort (mp);
  251.     }
  252.   else
  253.     perror ("CreatePort");
  254. }
  255.  
  256.  
  257. /* should help make things less mystic... */
  258. #define TP_SCALL(tp) (tp->tp_argv[0])
  259. /* this is only valid if !tp->tp_is_entry */
  260. #define TP_RESULT(tp) (tp->tp_argv[1])
  261. #define TP_ERROR(tp) (* tp->tp_errno)
  262. /* tp_argv[2] is the return address */
  263. #define TP_FIRSTARG(tp) (tp->tp_argv[3])
  264.  
  265. void
  266. print_call (FILE *output, struct trace_packet *tp)
  267. {
  268.   char line[OUT_WIDTH+2];    /* for \n\0 */
  269.   char *argfield;
  270.   int space, len;
  271.   struct call *c;
  272.  
  273.   space = sizeof (line) - 1;
  274.   len = sprintf (line, "$%lx: %c", (unsigned long) tp->tp_message.mn_ReplyPort,
  275.                tp->tp_is_entry ? '>' : '<');
  276.   argfield = line + len;
  277.   space -= len;
  278.  
  279.   if (TP_SCALL (tp) > sizeof (call_table) / sizeof (struct call))
  280.     {
  281.       if (tp->tp_is_entry)
  282.         sprintf (argfield, "SYS_%d()\n", TP_SCALL (tp));
  283.       else
  284.         sprintf (argfield, "SYS_%d() = $%lx (%d)\n", 
  285.           TP_SCALL (tp), (unsigned long) TP_RESULT (tp), TP_ERROR (tp));
  286.     }
  287.   else
  288.     {
  289.       c = call_table + TP_SCALL (tp);
  290.  
  291.       if ((!print_all && !c->interesting) || 
  292.       (skip_sigsetmask && TP_SCALL (tp) == SYS_sigsetmask))
  293.     return;
  294.  
  295.       /* we can write space-1 real characters in the buffer, \n is not counted */
  296.       c->func (argfield, space-1, c, tp);
  297.     }
  298.  
  299.   if (skip_calls == 0)
  300.     fputs (line, output);
  301.   else
  302.     skip_calls--;
  303. }
  304.  
  305. /* the program contained a bug due to the fact that
  306.  * when snprintf or vsnprintf are called with a zero or negative size,
  307.  * they return -1 as an error marker but not any length.
  308.  */
  309.  
  310. static void
  311. vp (char *buf, int len, struct call *c, struct trace_packet *tp)
  312. {
  313.   int nl = snprintf (buf, len+1, "%s", c->name);
  314.  
  315.   len -= nl; if (len <= 0) goto finish;
  316.   buf += nl;
  317.   if (tp->tp_is_entry || !c->rfmt[0])
  318.     vsnprintf (buf, len+1, c->fmt, (_BSD_VA_LIST_) & TP_FIRSTARG (tp));
  319.   else
  320.     {
  321.       nl = vsnprintf (buf, len+1, c->fmt, (_BSD_VA_LIST_) & TP_FIRSTARG (tp));
  322.       len -= nl; if (len <= 0) goto finish;
  323.       buf += nl;
  324.       nl = snprintf (buf, len+1, "=");
  325.       len -= nl; if (len <= 0) goto finish;
  326.       buf += nl;
  327.       nl = vsnprintf (buf, len+1, c->rfmt, (_BSD_VA_LIST_) & TP_RESULT (tp));
  328.       len -= nl; if (len <= 0) goto finish;
  329.       buf += nl;
  330.       nl = snprintf (buf, len+1, " (%d)", TP_ERROR (tp));
  331.     }
  332.  
  333.   finish:
  334.   strcat (buf, "\n");
  335. }
  336.  
  337. const char *
  338. get_fcntl_cmd (int cmd)
  339. {
  340.   switch (cmd)
  341.     {
  342.     case F_DUPFD:
  343.     return "F_DUPFD";
  344.  
  345.     case F_GETFD:
  346.     return "F_GETFD";
  347.  
  348.     case F_SETFD:
  349.     return "F_SETFD";
  350.  
  351.     case F_GETFL:
  352.     return "F_GETFL";
  353.  
  354.     case F_SETFL:
  355.     return "F_SETFL";
  356.     
  357.     case F_GETOWN:
  358.     return "F_GETOWN";
  359.  
  360.     case F_SETOWN:
  361.     return "F_SETOWN";
  362.  
  363. #ifdef F_GETLK
  364.     case F_GETLK:
  365.     return "F_GETLK";
  366. #endif
  367.  
  368. #ifdef F_SETLK
  369.     case F_SETLK:
  370.     return "F_SETLK";
  371. #endif
  372.  
  373. #ifdef F_SETLKW
  374.     case F_SETLKW:
  375.     return "F_SETLKW";
  376. #endif
  377.  
  378.     default:
  379.     return "F_unknown";
  380.     }
  381. }
  382.  
  383. char *
  384. get_open_mode (int mode)
  385. {
  386.   static char buf[120];
  387.   
  388.   switch (mode & O_ACCMODE)
  389.     {
  390.     case O_RDONLY: 
  391.       strcpy (buf, "O_RDONLY");
  392.       break;
  393.       
  394.     case O_WRONLY:
  395.       strcpy (buf, "O_WRONLY");
  396.       break;
  397.       
  398.     case O_RDWR:
  399.       strcpy (buf, "O_RDWR");
  400.       break;
  401.       
  402.     default:
  403.       strcpy (buf, "O_illegal");
  404.       break;
  405.     }
  406.  
  407. #define ADD(flag) \
  408.   if (mode & flag) strcat (buf, "|" #flag);
  409.  
  410.   ADD (O_NONBLOCK);  
  411.   ADD (O_APPEND);
  412.   ADD (O_SHLOCK);
  413.   ADD (O_EXLOCK);
  414.   ADD (O_ASYNC);
  415.   ADD (O_FSYNC);
  416.   ADD (O_CREAT);
  417.   ADD (O_TRUNC);
  418.   ADD (O_EXCL);
  419. #undef ADD
  420.   
  421.   return buf;
  422. }
  423.  
  424. char *
  425. get_ioctl_cmd (int cmd)
  426. {
  427.   static char buf[12];
  428.  
  429.   /* only deal with those commands that are really implemented somehow in
  430.      ixemul.library. The others are dummies anyway, so they don't matter */
  431.  
  432.   switch (cmd)
  433.     {
  434.     case FIONREAD:
  435.       return "FIONREAD";
  436.  
  437.     case FIONBIO:
  438.       return "FIONBIO";
  439.  
  440.     case FIOASYNC:
  441.       /* not yet implemented, but important to know if some program tries
  442.          to use it ! */
  443.       return "FIOASYNC";
  444.  
  445.     case TIOCGETA:
  446.       return "TIOCGETA";
  447.  
  448.     case TIOCSETA:
  449.       return "TIOCSETA";
  450.  
  451.     case TIOCSETAW:
  452.       return "TIOCSETAW";
  453.  
  454.     case TIOCSETAF:
  455.       return "TIOCSETAF";
  456.  
  457.     case TIOCGETP:
  458.       return "TIOCGETP";
  459.  
  460.     case TIOCSETN:
  461.       return "TIOCSETN";
  462.  
  463.     case TIOCSETP:
  464.       return "TIOCSETP";
  465.  
  466.     case TIOCGWINSZ:
  467.       return "TIOCGWINSZ";
  468.  
  469.     case TIOCOUTQ:
  470.       return "TIOCOUTQ";
  471.  
  472.     case TIOCSWINSZ:
  473.       return "TIOCSWINSZ";
  474.       
  475.     default:
  476.       sprintf (buf, "$%lx", (unsigned long) cmd);
  477.       return buf;
  478.     }
  479. }
  480.  
  481.  
  482. static void
  483. vp_fcntl (char *buf, int len, struct call *c, struct trace_packet *tp)
  484. {
  485.   int *argv =  & TP_FIRSTARG (tp);
  486.   
  487.   if (tp->tp_is_entry)
  488.     if (argv[1] == F_GETFL || argv[1] == F_SETFL)
  489.       snprintf (buf, len+1, "fcntl(%d, %s, %s)",
  490.             argv[0], get_fcntl_cmd (argv[1]), get_open_mode (argv[2]));
  491.     else
  492.       snprintf (buf, len+1, "fcntl(%d, %s, %d)",
  493.             argv[0], get_fcntl_cmd (argv[1]), argv[2]);
  494.   else
  495.     if (argv[1] == F_GETFL || argv[1] == F_SETFL)
  496.       snprintf (buf, len+1, "fcntl(%d, %s, %s) = %d (%d)",
  497.             argv[0], get_fcntl_cmd (argv[1]), 
  498.         get_open_mode (argv[2]), TP_RESULT (tp), TP_ERROR (tp));
  499.     else
  500.       snprintf (buf, len+1, "fcntl(%d, %s, %d) = %d (%d)",
  501.             argv[0], get_fcntl_cmd (argv[1]), argv[2], 
  502.         TP_RESULT (tp), TP_ERROR (tp));
  503.  
  504.   strcat (buf, "\n");
  505. }
  506.  
  507.  
  508. static void
  509. vp_ioctl (char *buf, int len, struct call *c, struct trace_packet *tp)
  510. {
  511.   int *argv = & TP_FIRSTARG (tp);
  512.   
  513.   if (tp->tp_is_entry)
  514.     snprintf (buf, len+1, "ioctl(%d, %s, $%lx)",
  515.           argv[0], get_ioctl_cmd (argv[1]), argv[2]);
  516.   else
  517.     snprintf (buf, len+1, "ioctl(%d, %s, $%lx) = %d (%d)",
  518.           argv[0], get_ioctl_cmd (argv[1]), argv[2],
  519.           TP_RESULT (tp), TP_ERROR (tp));
  520.  
  521.   strcat (buf, "\n");
  522. }
  523.  
  524.  
  525. static void
  526. vp_open (char *buf, int len, struct call *c, struct trace_packet *tp)
  527. {
  528.   int *argv = & TP_FIRSTARG (tp);
  529.  
  530.   if (tp->tp_is_entry)
  531.     snprintf (buf, len+1, "open(\"%s\", %s)", argv[0], get_open_mode (argv[1]));
  532.   else
  533.     snprintf (buf, len+1, "open(\"%s\", %s) = %d (%d)", argv[0], 
  534.           get_open_mode (argv[1]), TP_RESULT (tp), TP_ERROR (tp));
  535.  
  536.   strcat (buf, "\n");
  537. }
  538.  
  539. static void
  540. vp_pipe (char *buf, int len, struct call *c, struct trace_packet *tp)
  541. {
  542.   int *argv = & TP_FIRSTARG (tp);
  543.  
  544.   if (tp->tp_is_entry)
  545.     snprintf (buf, len+1, "pipe($%lx)", argv[0]);
  546.   else
  547.     {
  548.       int *pv = (int *) argv[0];
  549.     
  550.       snprintf (buf, len+1, "pipe([%d, %d]) = %d (%d)", 
  551.         pv[0], pv[1], TP_RESULT (tp), TP_ERROR (tp));
  552.     }
  553.  
  554.   strcat (buf, "\n");
  555. }
  556.